package com.fdb.smcts.core.util;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;

import org.slf4j.LoggerFactory;

import com.alibaba.dubbo.common.utils.PojoUtils;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fdb.smcts.core.annotation.BeanMapKey;

/**
 * 
 * hsf泛型调用转换工具类
 * 
 * @author Cytus_
 * @since 2018年5月16日 下午4:57:48
 * @version 1.0
 *
 */
public final class HsfGenericUtils {

	public final static String CLASS_STRING = "class";

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static final Object exchangeRtnData(Object hsfRtnObj) throws Exception {
		Object rtnObj = null;

		String clazzName = "java.lang.Object";
		if (hsfRtnObj instanceof Map) {
			clazzName = (String) ((Map) hsfRtnObj).get(CLASS_STRING);
		}

		if (clazzName.matches(".+Exception$")) {
			rtnObj = map2Exception((Map) hsfRtnObj);
			if (Objects.nonNull(rtnObj)) {
				throw (Exception) rtnObj;
			} else {
				LoggerFactory.getLogger(HsfGenericUtils.class).error("解析异常对象出错!+\n\r" + hsfRtnObj);
			}
		} else {
			rtnObj = PojoUtils.realize(hsfRtnObj, Class.forName(clazzName));
		}

		return rtnObj;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static final Object jsonExchangeRtnData(Object hsfRtnObj) throws Exception {
		if (hsfRtnObj instanceof Map) {
			Map rtnMap = (Map) hsfRtnObj;
			Object rtnObj = null;
			if (rtnMap.containsKey(CLASS_STRING)) {
				rtnObj = Class.forName((String) rtnMap.get(CLASS_STRING)).newInstance();
				JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(rtnMap));
				rtnObj = JSONObject.toJavaObject(jsonObject, rtnObj.getClass());
			}
			return rtnObj;
		} else if (hsfRtnObj instanceof List) {
			List list = null;
			JSONArray jsonArray = JSONObject.parseArray(JSONObject.toJSONString(hsfRtnObj));
			if (Objects.nonNull(jsonArray) && !jsonArray.isEmpty()) {
				Class listGenrClass = Class.forName((String) (jsonArray.getJSONObject(0)).get(CLASS_STRING));
				list = JSONObject.parseArray(jsonArray.toJSONString(), listGenrClass);
			}
			return list;
		}
		return hsfRtnObj;
	}

	/**
	 * map 转bean 通过注解
	 * 
	 * @param dataMap
	 * @param clazz
	 * @return
	 * @throws Exception
	 */
	public static Object map2Bean(Map<String, Object> dataMap) throws Exception {
		Object object = null;
		String clazzName = (String) dataMap.get(CLASS_STRING);
		if (StringUtil.isNotEmpty(clazzName)) {
			if (clazzName.matches(".+Exception$")) {
				object = newExceptionObject(clazzName, (String) dataMap.get("message"));
				exceptionMap2Bean(dataMap, object);
			} else {
				object = newObject(clazzName);
				object = map2Object(dataMap, object);
			}
		}

		return object;
	}

	public static Object map2Exception(Map<String, Object> dataMap) throws Exception {
		Object object = null;
		String clazzName = (String) dataMap.get(CLASS_STRING);
		if (StringUtil.isNotEmpty(clazzName)) {
			object = newExceptionObject(clazzName, removePrefix((String) dataMap.get("message")));
			exceptionMap2Bean(dataMap, object);
		}
		return object;
	}

	@SuppressWarnings("unchecked")
	public static Object map2Object(Map<String, Object> dataMap, Object object) throws Exception {
		if (Objects.nonNull(dataMap) && !dataMap.isEmpty() && Objects.nonNull(object)) {
			for (String key : dataMap.keySet()) {

				Object valObj = dataMap.get(key);
				if (Objects.nonNull(valObj)) {
					if (valObj instanceof List) {
						List<Object> valObjList = (List<Object>) valObj;
						valObj = list2Bean(valObjList);
					} else if (valObj instanceof Map) {
						valObj = map2Bean((Map<String, Object>) valObj);
					}
					try {
						setValue(object, key, valObj);
					} catch (Exception e) {
					}
				}

			}
		}
		return object;
	}

	public static Object exceptionMap2Bean(Map<String, Object> dataMap, Object object) throws Exception {

		if (Objects.nonNull(dataMap) && !dataMap.isEmpty() && Objects.nonNull(object)) {
			for (String key : dataMap.keySet()) {
				Object valObj = dataMap.get(key);

				if ("stackTrace".equals(key)) {
					valObj = getstackTrace(valObj);
				}

				if ("message".equals(key)) {
					valObj = removePrefix(StringUtil.replaceObjNull(valObj));
				}

				if (Objects.nonNull(valObj)) {
					try {
						setValue(object, key, valObj);
					} catch (Exception e) {
					}
				}

			}
		}

		return object;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private static StackTraceElement[] getstackTrace(Object obj) {
		Collection<Object> collStacks = null;
		if (obj instanceof Collection) {
			collStacks = (Collection<Object>) obj;
		} else if (obj.getClass().isArray()) {
			collStacks = Arrays.asList((Object[]) obj);
		} else {
			return null;
		}
		List<StackTraceElement> stacks = new LinkedList<StackTraceElement>();
		for (Object stack : collStacks) {
			Map<?, ?> dataMap = (Map) stack;
			String lineNumber = StringUtil.replaceObjNull(dataMap.get("lineNumber"));
			stacks.add(new StackTraceElement(StringUtil.replaceObjNull(dataMap.get("className")),
					StringUtil.replaceObjNull(dataMap.get("methodName")), StringUtil.replaceObjNull(dataMap.get("fileName")),
					NumberUtility.isInteger(lineNumber) ? Integer.valueOf(lineNumber) : -1));
		}
		return stacks.toArray(new StackTraceElement[stacks.size()]);
	}

	public static Object newObject(String clazzName) throws Exception {
		return Class.forName(clazzName).newInstance();
	}

	@SuppressWarnings("rawtypes")
	private static Object newExceptionObject(String clazzName, String message) throws Exception {
		Object obj = null;
		Class<?> clazz = Class.forName(clazzName);
		try {
			Constructor c = clazz.getConstructor(Throwable.class);
			obj = c.newInstance(new Exception(message));
		} catch (Exception e) {
		}
		if (Objects.isNull(obj)) {
			try {
				Constructor c = clazz.getConstructor(String.class);
				obj = c.newInstance(message);
			} catch (Exception e) {
			}
		}
		return obj;
	}

	private final static String removePrefix(String message) {
		try {
			return Pattern.compile("Exception From Host:\\[{1}[0-9]*\\.[0-9]*\\.[0-9]*\\.[0-9]*\\]{1}").matcher(message).replaceAll("");
		} catch (Exception e) {
		}
		return message;
	}

	/**
	 * 配合{@link map2Bean}使用
	 * 
	 * @param bean
	 * @param fieldKey
	 * @param objVal
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public static void setValue(Object bean, String fieldKey, Object objVal) throws Exception {
		Field field = getFiled(bean, fieldKey);
		if (Objects.nonNull(field)) {
			field.setAccessible(true);
			if (Enum.class.isAssignableFrom(field.getType())) {
				objVal = Enum.valueOf((Class) field.getType(), StringUtil.replaceObjNull(objVal));
			}
			field.set(bean, objVal);
		}
	}

	@SuppressWarnings("rawtypes")
	public static Field getFiled(Object bean, String key) {
		List<Field> list = new ArrayList<Field>();
		Class clazz = bean.getClass();
		while (Objects.nonNull(clazz) && !clazz.getName().equals("java.lang.Object")) {
			list.clear();
			list.addAll(Arrays.asList(clazz.getDeclaredFields()));
			Field field = list.parallelStream().filter(s -> getFiled(s, key)).findFirst().orElse(null);
			if (Objects.nonNull(field)) {
				return field;
			}
			clazz = clazz.getSuperclass();
		}
		return null;
	}

	public static boolean getFiled(Field field, String key) {
		JsonProperty jsonProperty = field.getAnnotation(JsonProperty.class);
		if (Objects.nonNull(jsonProperty)) {
			return key.equals(jsonProperty.value());
		}

		BeanMapKey beanMapKey = field.getAnnotation(BeanMapKey.class);
		if (Objects.nonNull(beanMapKey)) {
			return key.equals(beanMapKey.value());
		}

		return field.getName().equals(key);

	}

	/**
	 * 配合{@link map2Bean}使用
	 * 
	 * @param list
	 * @param clazz
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public static List<Object> list2Bean(List<Object> list) throws Exception {

		List<Object> dataList = new ArrayList<Object>();
		if (Objects.nonNull(list) && !list.isEmpty()) {
			if (list.get(0) instanceof Map) {
				for (Object obj : list) {
					Map<String, Object> dataMap = (Map<String, Object>) obj;
					dataList.add(map2Bean(dataMap));
				}
			} else {
				dataList.add(list);
			}

		}
		return dataList;
	}

}
